import * as THREE from 'three';
import M from 'scripts/main.js';

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { FontLoader } from 'three/addons/loaders/FontLoader.js';
import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
import { TWEEN } from 'three/addons/libs/tween.module.min.js';
			
import {GameList} from 'scripts/gameList.js';

class Game {
	constructor(index,gameID){
		//TODO:根据课本的稀有度，调整挑战的难度，
		//射箭类2级将靶子缩小50%，3级移动靶，4级小移动靶，5级小移动靶无准星
		//冰壶类每提高一级，时间缩短20%
		//得到的荣誉值也乘以相应的倍数
		const items =[
			{id:1,name:'听音找字',type:1,group:1},
			{id:2,name:'看字读音',type:2,group:1},
			{id:3,name:'看字找意',type:1,group:1},
			{id:4,name:'看意找字',type:1,group:1},
			{id:5,name:'听字找意',type:1,group:1},
			{id:6,name:'听音找词',type:1,group:2},
			{id:7,name:'看词读音',type:2,group:2},
			{id:8,name:'看词找意',type:1,group:2},
			{id:9,name:'看意找词',type:1,group:2},
			{id:10,name:'听词找意',type:1,group:2},
			{id:11,name:'看意找句',type:1,group:3},
			{id:12,name:'看句读音',type:2,group:3},
			{id:13,name:'听音找句',type:1,group:3},
			{id:14,name:'看音找字',type:1,group:4},
			{id:15,name:'看音读音',type:2,group:4},
			{id:16,name:'看字找音',type:1,group:4},
		];
		
		const m = new M();
		let scene = new THREE.Scene();
		let camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 );
		let fillLight1 = new THREE.HemisphereLight( 0x4488bb, 0x002244, 0.5 );
		let directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
		let container = document.getElementById( 'container' );
		let renderer = new THREE.WebGLRenderer( { antialias: true } );

		let books,book,timerBar, timerVal=100, arrowDirection, overall,pinyin={};
		let playTimerID,gameTimerID,animationID;
		let mouseDown = false,isMan=true ,isRight=false,archery,fly=0,playing,gaming,question,score,offset,answer;
		let bow,arrow,curling,collimator,font,otherFont,pinyinFont,scoreboard,questionBoard,objects=[];
		let sound = [],ziVoice=[],ciVoice=[],juVoice=[],targets=[],randomArr=[];
		init();
		
		/*
		* 初始化
		*/
		async function init(){
			THREE.Cache.enabled = true;
			m.display("goback",true);
			m.getNode("goback").addEventListener('pointerdown', goback);
			books = m.loadItem("books");
			books = JSON.parse(books);
			for(let i=0;i<books.length;i++){
				if (books[i].index == index) {
					book = books[i];
					break;
				}
			}
			//载入楷体的字体
			font = await loadFont('KaiTi');
			//载入对应语言的字体
			otherFont = await loadFont('optimer');
			if (items[gameID-1].group==4) pinyinFont = await loadFont('pinyin');
			//时间条
			const geometry = new THREE.CylinderGeometry( 0.01, 0.01, 1, 32 );
			const material = new THREE.MeshBasicMaterial( {color: getColor(100)} );
			timerBar = new THREE.Mesh( geometry, material );
			timerBar.position.set(0,0.8,-1.6);
			timerBar.rotation.set(0,0,Math.PI/2);
			scene.add( timerBar );
			//成绩牌
			scoreboard = new THREE.Mesh( new THREE.PlaneGeometry( 5, 1 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
			scoreboard.position.set( 0, 4.5, -8 );
			scene.add( scoreboard );
			setScore();
			//题问牌
			questionBoard = new THREE.Mesh( new THREE.PlaneGeometry( 0.5, 0.25 ), new THREE.MeshBasicMaterial( { color: 0xffd800 } ) );
			questionBoard.position.set( 0, -0.3, 1 );
			scene.add( questionBoard );
			//场景背景设为蓝色			
			scene.background = new THREE.Color( 0x88ccee );
			sound.push(new Howl({src: [m.current()+'sounds/'+'啊哦.mp3']}));
			
			let jpg = "";
			if (items[gameID-1].type==1){
				if (gameID==1 || gameID==5) loadZiVoice();
				if (gameID==6 || gameID==10) loadCiVoice();
				sound.push(new Howl({src: [m.current()+'sounds/'+'射中.mp3']}));
				sound.push(new Howl({src: [m.current()+'sounds/'+'射偏.mp3']}));
				jpg = 'grass.jpg';
				bow = await loadGlb('弓');
				bow.position.set( 0.15, -0.6, 0.2 );
				bow.rotation.x=-1;

				arrow = await loadGlb('箭');
				arrow.position.set(0.15, -0.6, 10 );
				arrow.rotation.y= -90*Math.PI/180;

				//藏在靶子后面的透明幕布，主要目的是给准星一个计算坐标的标的
				const backScreen = new THREE.Mesh( new THREE.PlaneGeometry( 100, 100 ), new THREE.MeshBasicMaterial( { color: 0x88ccee,transparent: true, opacity: 0 } ) );
				scene.add( backScreen );
				backScreen.position.set( 0, 0, -10.01 );
				objects.push(backScreen);
				//靶子
				makeTarget();
				targets[0].position.set(0,0.5,-10);
				updateTarget("开始",font,0.7,0);
				//准星			
				collimator = makeCollimator();
				window.addEventListener( 'mousedown', onMousedown );
				window.addEventListener( 'touchstart', onMousedown );
				window.addEventListener( 'mouseup', onMouseup );
				window.addEventListener( 'touchend', onMouseup );
				window.addEventListener( 'mousemove', onMousemove );
				window.addEventListener( 'touchmove', onMousemove );
			}else{
				//打开语音评测，请求录音
				m.recOpen();
				sound.push(new Howl({src: [m.current()+'sounds/'+'喝彩.mp3']}));
				jpg = 'floor.jpg';
				curling = await loadGlb('冰壶');
				curling.position.set( 2, -1.8, -3.5 );
				curling.scale.set( 0.3, 0.3, 0.3 );
				curling.rotation.set( 0, Math.PI/2, 0 );
				objects.push(curling);
				//滑道
				makeTrake();
				questionBoard.position.set(  0, 0.1, -1 );
				setQuestion("按住朗读\n读完松手",font,0.06);

				window.addEventListener( 'mousedown', onPointerdown );
				window.addEventListener( 'touchstart', onPointerdown );
				window.addEventListener( 'mouseup', onPointerup );
				window.addEventListener( 'touchend', onPointerup );
				window.addEventListener( 'mousemove', onPointermove );
				window.addEventListener( 'touchmove', onPointermove );
			}

			//相机转动顺序
			camera.rotation.order = 'YXZ';
			//环境光
			fillLight1.position.set( 2, 1, 1 );
			scene.add( fillLight1 );

			//带投影的光
			directionalLight.position.set( - 5, 25, - 1 );
			directionalLight.castShadow = true;
			directionalLight.shadow.camera.near = 0.01;
			directionalLight.shadow.camera.far = 500;
			directionalLight.shadow.camera.right = 30;
			directionalLight.shadow.camera.left = - 30;
			directionalLight.shadow.camera.top	= 30;
			directionalLight.shadow.camera.bottom = - 30;
			directionalLight.shadow.mapSize.width = 1024;
			directionalLight.shadow.mapSize.height = 1024;
			directionalLight.shadow.radius = 4;
			directionalLight.shadow.bias = - 0.00006;
			scene.add( directionalLight );
			//渲染器
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			renderer.shadowMap.enabled = true;
			renderer.shadowMap.type = THREE.VSMShadowMap;
			renderer.outputEncoding = THREE.sRGBEncoding;
			renderer.toneMapping = THREE.ACESFilmicToneMapping;
			container.appendChild( renderer.domElement );
			//地面
			const gt = new THREE.TextureLoader().load( m.current()+'images/'+jpg );
			gt.wrapS = gt.wrapT = THREE.RepeatWrapping; 
			gt.repeat.set(10, 15); 
			const gm = new THREE.MeshPhongMaterial( { color: 0xffffff, map: gt } );
			const ground = new THREE.Mesh( new THREE.BoxGeometry( 200, 1, 200, 1, 1, 1 ), gm );
			ground.position.set(0,-10,0);
			scene.add(ground);
			
			window.addEventListener( 'resize', onWindowResize );
			animate();

		}
		/*
		* 返回竞技场
		*/
		function goback(){
			destroy();
			new GameList(book);
		}		
		/*
		* 开始新游戏
		*/
		function newGame(){
			randomArr=[];
			question=0;
			score=0;
			gaming=true;
			if (items[gameID-1].type==1){
				//射箭类
				arrow.position.set( 0.15, -0.6, 10 );
				arrow.rotation.set(0,0,0);
				for(let i=0;i<4;i++){
					targets[i].position.set(i*3-5, 0.5, -10);
				}
			}else{
				//冰壶类
				curling.position.set( 2, -1.8, -4 );
				questionBoard.position.set( 0, 0.1, -1 );
			}
			setScore(0);
			next();
		}
		/*
		* 下一题
		*/
		function next(){
			if (playing) playing.stop();
			clearTimeout(playTimerID);
			timerVal=100;
			setTimebar(100);
			question++;
			isRight=false;
			if (arrow) {
				arrow.position.set( 0.15, -0.6, 10 );
				//接受鼠标响应
				window.addEventListener( 'mousedown', onMousedown);
				window.addEventListener( 'touchstart', onMousedown);
				window.addEventListener( 'mouseup', onMouseup);
				window.addEventListener( 'touchend', onMouseup);
			}else{
				camera.position.set(0,0,0);
				camera.rotation.set(0,0,0);
				curling.position.set( 2, -1.8, -4 );
				window.addEventListener( 'mousedown', onPointerdown );
				window.addEventListener( 'touchstart', onPointerdown );
				window.addEventListener( 'mouseup', onPointerup );
				window.addEventListener( 'touchend', onPointerup );
			}
			if (question>10) endGame();
			else{
				//开始计时
				gameTimerID = setTimeout(gameTimer,100);
				eval("game"+gameID+"()");
			}
		}
		/*
		* 载入所有字的读音
		*/
		function loadZiVoice(){
			if (ziVoice.length==0){
				for ( let i = 0; i < book.zis.length; i ++ ) {
					const audiom = new Howl({
					  src: ['https://debug.smwho.com/sound/zi/m/'+book.zis[i].sound+'.mp3'],
					  autoplay: false,
					  onend:ziVoiceEnd
					});
					const audiof = new Howl({
					  src: ['https://debug.smwho.com/sound/zi/f/'+book.zis[i].sound+'.mp3'],
					  autoplay: false,
					  onend:ziVoiceEnd
					});
					ziVoice.push(audiom);
					ziVoice.push(audiof);
				}
			}
		}
		/*
		* 载入所有词的读音
		*/
		function loadCiVoice(){
			if (ciVoice.length==0){
				for ( let i = 0; i < book.cis.length; i ++ ) {
					let cid = book.cis[i].id+"";
					const audiom = new Howl({
					  src: ['https://debug.smwho.com/sound/ci/m/'+cid.substr(0,2)+"/"+cid.substr(2)+'.mp3'],
					  autoplay: false,
					  onend:ciVoiceEnd
					});
					const audiof = new Howl({
					  src: ['https://debug.smwho.com/sound/ci/f/'+cid.substr(0,2)+"/"+cid.substr(2)+'.mp3'],
					  autoplay: false,
					  onend:ciVoiceEnd
					});
					ciVoice.push(audiom);
					ciVoice.push(audiof);
				}
			}
		}
		/*
		* 循环播放字
		*/
		function ziVoiceEnd(){
			isMan=!isMan;
			playTimerID = setTimeout(()=>{
				playing = ziVoice[answer*2+(isMan?0:1)];
				playing.play();
			},1500);
		}
		/*
		* 循环播放词
		*/
		function ciVoiceEnd(){
			isMan=!isMan;
			playTimerID = setTimeout(()=>{
				playing = ciVoice[answer*2+(isMan?0:1)];
				playing.play();
			},2000);
		}
		/*
		* 循环播放句
		*/
		function juVoiceEnd(){
			playTimerID = setTimeout(()=>{
				playing.play();
			},2000);
		}
		/*
		* 跟随弓箭角度调整准星
		*/
		function moveCollimator(){
			let vector = bow.position.clone();
			//不会用Vector3的向量运算，就用最老土的办法实现箭头指向角度的normalize
			//根据球面公式，应该很容易计算出三个点的坐标，可是我算出来的居然是反的
			let x = Math.sin(bow.rotation.y)*Math.cos(bow.rotation.x);
			//按照书上的公式应该还有后面那一段的，可是我加上却算出来的不对，不知道什么原因。
			let y = Math.sin(bow.rotation.x);//*Math.sin(bow.rotation.y);
			let z = Math.cos(bow.rotation.y);
			arrowDirection = new THREE.Vector3(-x,y,-z);
			//以弓箭的原点和转动角度的归一化点发出一条射线，检测与背景幕布和靶子的交叉点
			const raycaster = new THREE.Raycaster(vector,arrowDirection);
			const intersections = raycaster.intersectObjects(objects);
			const p = intersections[0].point;
			//将准星移动到射线交点的位置
			collimator.position.set(p.x,p.y,-10);
			//返回交点上的对象
			return intersections[0];
		}
		/*
		* 提起冰壶
		*/
		function onPointerdown(e){
			if (checkEnergy()){
				let x,y;
				if (e.clientX){
					x=e.clientX;
					y=e.clientY;
				}else{
					x = e.changedTouches[0].pageX;
					y = e.changedTouches[0].pageY;
				} 
				const rect = renderer.domElement.getBoundingClientRect();
				//鼠标点击位置
				const pointer = new THREE.Vector2();
				//计算屏幕坐标
				pointer.x = ( x - rect.left ) / rect.width * 2 - 1;
				pointer.y = - ( y - rect.top ) / rect.height * 2 + 1;
				//设置当前视角和点击位置
				const raycaster = new THREE.Raycaster();
				raycaster.setFromCamera( pointer, camera );
				//获取这条射线上所有交叉的点
				const intersections = [];
				raycaster.intersectObjects( objects ,true,intersections);
				if ( intersections.length > 0 ) {
					mouseDown = {x:x};
					curling.position.y=-1.6;
					if (gaming){
						m.recStart(pinyin.type,pinyin.z,pinyin.p);
					}
				}
			}
		}
		/*
		* 移动冰壶
		*/
		function onPointermove(e){
			if (mouseDown){
				let x;
				if (e.clientX) x=e.clientX;
				else x = e.changedTouches[0].pageX;
				//不知道为什么移动的偏移量是和屏幕高度有关的，这个4.6也不见得准确
				curling.position.x -= (mouseDown.x - x)/(window.innerHeight/4.6);
				mouseDown = {x:x};
			}
		}
		/*
		* 放开冰壶
		*/
		function onPointerup(e){
			if (mouseDown){
				clearTimeout(gameTimerID);
				curling.position.y = -1.8;
				mouseDown = null;
				if (Math.abs(curling.position.x)>1){
					alert('请放在滑道上');
					curling.position.x = 2;
					return;
				}
				if(gaming) recStop();
				TWEEN.removeAll();
				new TWEEN.Tween( curling.position )
					.to( { x: curling.position.x, y: curling.position.y, z: -13 }, 1000 )
					.easing( TWEEN.Easing.Exponential.Out )
					.onComplete(stoped)
					.onUpdate( render )
					.start();
				window.removeEventListener( 'mouseup', onPointerup );
				window.removeEventListener( 'touchend', onPointerup );
				window.removeEventListener( 'mousedown', onPointerdown );
				window.removeEventListener( 'touchstart', onPointerdown );
			}
		}
		/*
		* 冰壶或箭停止了
		*/
		function stoped(){
			if(gaming){
				if (items[gameID-1].type==2){
					moveCamera();
				}else{
					if (isRight) setTimeout(scoring,500);
					else{
						//只有在脱靶的情况下隐藏箭矢
						if(archery.object.parent==scene) arrow.position.set( 0.15, -0.6, 10 );
						setTimeout(wrong,500);
					}
					archery = null;
				}
			}else{
				if (items[gameID-1].type==2) newGame();
				else if (isRight) setTimeout(newGame,1000);
				else {
					arrow.position.set( 0.15, -0.6, 10 );
					window.addEventListener( 'mousedown', onMousedown);
					window.addEventListener( 'touchstart', onMousedown);
					window.addEventListener( 'mouseup', onMouseup);
					window.addEventListener( 'touchend', onMouseup);
				}
			}
		}
		/*
		* 移动镜头展示结果
		*/
		function moveCamera(){
			//因为停止的时候语音评测可能还没有得到结果，所以每个0.1秒检查一次
			if (isRight){
				//播放欢呼声
				if(overall>50) sound[1].play();
				//根据得分放置冰壶位置
				curling.position.z = -13-overall/100;
				camera.position.set(0,0,-13);
				camera.rotation.set(-1.3,0,0);
				setTimeout(next,1000);
			}else setTimeout(moveCamera,100);
		}
		/*
		* 停止语音评测
		*/
		async function recStop(){
			let data = await m.recStop();
			//TODO：评测异常处理
			
			//TODO：更新课本的磨损度

			//TODO:将录音文件和得分数据保存下来
			let url = data.audioUrl;
			overall = data.result.overall;
			if (data.result.overall>0){
				//分值=剩余时间 X 评测分数% X 放下位置的偏移%
				score += Math.floor(timerVal*overall/100*(1-curling.position.x));
				setScore(score);
			}
			isRight = true;
		}		
		/*
		* 移动弓箭瞄准
		*/
		function onMousemove(e){
			if (mouseDown){
				let x,y;
				if (e.clientX){
					//只有鼠标移动才有这个属性
					x=e.movementX;
					y=e.movementY;
				}else{
					//因此触屏只能手动计算
					x = e.changedTouches[0].pageX - mouseDown.changedTouches[0].pageX;
					y = e.changedTouches[0].pageY - mouseDown.changedTouches[0].pageY;
				} 
				mouseDown = e;
				bow.rotation.y -= x / 500;
				bow.rotation.x -= y / 500;
				//限制移动范围
				if (bow.rotation.y<-0.6) bow.rotation.y = -0.6;
				if (bow.rotation.y>0.7) bow.rotation.y = 0.7;
				if (bow.rotation.x<-0.1) bow.rotation.x = -0.1;
				if (bow.rotation.x>0.2) bow.rotation.x = 0.2;
				//绘制准星
				moveCollimator();
			}
		}
		/*
		* 放手射箭
		*/
		function onMouseup(event){
			//暂停鼠标响应
			window.removeEventListener( 'mousedown', onMousedown);
			window.removeEventListener( 'touchstart', onMousedown);
			window.removeEventListener( 'mouseup', onMouseup);
			window.removeEventListener( 'touchend', onMouseup);
			mouseDown = null;
			clearTimeout(gameTimerID);
			clearTimeout(playTimerID);
			if(playing) playing.stop();
			//设置箭的初始位置
			arrow.position.set(0, -0.6, 0.2 );
			arrow.rotation.x=bow.rotation.x;
			arrow.rotation.y=bow.rotation.y;
			arrow.rotation.z=bow.rotation.z;
			//获取最终瞄准的位置
			archery = moveCollimator();
			//弓复位
			bow.position.set( 0.0, -0.6, 0.2 );
			bow.rotation.set(-1,0,0);
			//准星复位
			collimator.position.set(0,0,0);
			if (archery.object.parent==scene) sound[2].play();//脱靶
			else {
				//上靶
				sound[1].play();
				if (gaming){
					//计算射中位置和圆心的距离
					var pos = new THREE.Vector3();
					archery.object.getWorldPosition( pos );
					offset = archery.point.distanceTo (pos);
					//判断是否正确选项
					isRight = (archery.object.parent.tag == answer);
				}else {
					//开始游戏
					isRight = true;
				}
			}
			if (archery){
				//箭的飞行动画
				TWEEN.removeAll();
				//因为箭的长度有1，箭的中心在尾部，所以-1
				let tagert = archery.point.addScaledVector( arrowDirection, -1 );
				//如果脱靶了让箭多飞5米，
				if (archery.object.parent==scene) tagert = archery.point.addScaledVector( arrowDirection, 5 );
				//为什么y轴要-0.5我也不清楚，至于0.5是不是最佳值，也不得而知
				tagert.y-=0.5;
				new TWEEN.Tween( arrow.position )
					.to( tagert, isRight?100:200 )
					.onComplete(stoped)
					.onUpdate( render )
					.start();
			}
		}
		/*
		* 开始提弓瞄准
		*/
		function onMousedown(e){
			if (checkEnergy()){
				mouseDown = e;
				//显示提起的弓
				bow.position.set( 0.15, -0.6, 0 );
				bow.rotation.set(0,0,0);
				//隐藏箭
				arrow.position.set( 0.15, -0.6, 10 );
				arrow.rotation.set(0,0,0);
				//显示准星
				moveCollimator();
				render();
			}
		}
		/*
		* 检查和消耗体力
		*/
		function checkEnergy(){
			let energy = m.loadInt("energy");
			if(gaming){
				energy--;
				m.setEnergy(energy);
			}else{
				if (energy<10){
					alert("体力不足，请先补充体力，再来挑战");
					return false;
				}
			}
			return true;
		}
		/*
		* 更新等待时间的进度条
		*/
		function gameTimer(){
			//句子类的问题答题时间为100秒，其余的为10秒
			if (items[gameID-1].group==3) timerVal-=0.1;
			else timerVal--;
			setTimebar(timerVal);
			if(timerVal>0) gameTimerID = setTimeout(gameTimer,100);
			else wrong();//答题超时
			render();			
		}
		/*
		* 绘制准星
		*/
		function makeCollimator(){
			const group = new THREE.Group();
			//四条线
			const line1Pts = [new THREE.Vector2( 0, 0.05 ),new THREE.Vector2( 0, 0.17 )];
			const line1Shape = new THREE.Shape( line1Pts );

			const line2Pts = [new THREE.Vector2( 0, -0.05 ),new THREE.Vector2( 0, -0.17 )];
			const line2Shape = new THREE.Shape( line2Pts );

			const line3Pts = [new THREE.Vector2( 0.05, 0 ),new THREE.Vector2( 0.17, 0 )];
			const line3Shape = new THREE.Shape( line3Pts );

			const line4Pts = [new THREE.Vector2( -0.05, 0 ),new THREE.Vector2( -0.17, 0 )];
			const line4Shape = new THREE.Shape( line4Pts );
			//一个圆
			const arcShape = new THREE.Shape()
				.absarc( 0, 0, 0.2, 0, Math.PI * 2, false );

			addLineShape( line1Shape, 0x000000,group);
			addLineShape( line2Shape, 0x000000,group);
			addLineShape( line3Shape, 0x000000,group);
			addLineShape( line4Shape, 0x000000,group);
			addLineShape( arcShape, 0xff0000 ,group);
			scene.add( group );
			//内置方法，根据点画线
			function addLineShape( shape, color, group) {

				const points = shape.getPoints();
				const geometryPoints = new THREE.BufferGeometry().setFromPoints( points );

				let line = new THREE.Line( geometryPoints, new THREE.LineBasicMaterial( { color: color } ) );
				group.add( line );
			}
			return group;
		}
		/*
		* 绘制靶子
		*/
		function makeTarget(){
			for(let i=0;i<4;i++){
				const group = new THREE.Group();
				const materials = new THREE.MeshBasicMaterial( { color: 0x000000 } );
				//挂靶子的两条线
				const line1 = new THREE.Mesh( new THREE.PlaneGeometry( 1, 0.03 ), materials );
				group.add( line1 );
				line1.position.set( 0.6, 1, 0 );
				line1.rotation.set( 0, 0, 1 );
				const line2 = new THREE.Mesh( new THREE.PlaneGeometry( 1, 0.03 ), materials );
				group.add( line2 );
				line2.position.set( -0.6, 1, 0 );
				line2.rotation.set( 0, 0, -1 );

				//靶子从外到里五种颜色
				const white = new THREE.Mesh( new THREE.CircleGeometry( 1, 32 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
				const black = new THREE.Mesh( new THREE.CircleGeometry( 0.8, 32 ), new THREE.MeshBasicMaterial( { color: 0x000000 } ) );
				const blue = new THREE.Mesh( new THREE.CircleGeometry( 0.6, 32 ), new THREE.MeshBasicMaterial( { color: 0x00ffff } ) );
				const red = new THREE.Mesh( new THREE.CircleGeometry( 0.4, 32 ), new THREE.MeshBasicMaterial( { color: 0xff0000 } ) );
				const yellow = new THREE.Mesh( new THREE.CircleGeometry( 0.2, 32 ), new THREE.MeshBasicMaterial( { color: 0xffd800 } ) );
				group.add( white );
				group.add( black );
				group.add( blue );
				group.add( red );
				group.add( yellow );
				//响应碰撞检测的对象，所以需要添加
				objects.push(white);
				objects.push(black);
				objects.push(blue);
				objects.push(red);
				objects.push(yellow);
				//显示题目的牌子
				const plane = new THREE.Mesh( new THREE.PlaneGeometry( 2.2, 2.2 ), new THREE.MeshBasicMaterial( { color: 0xffd800 } ) );
				group.add( plane );
				plane.position.set( 0, 2.5, 0 );
				
				scene.add( group );
				targets.push(group);
				group.position.set( 0, 0.5, 10 );
			}
		}
		/*
		* 绘制滑道
		*/
		function makeTrake(){
			const group = new THREE.Group();

			//滑道
			const plane1 = new THREE.Mesh( new THREE.PlaneGeometry( 2.4, 12.4 ), new THREE.MeshBasicMaterial( { color: 0x602600 } ) );
			plane1.position.set( 0,5, 0.0001 );
			group.add( plane1 );
			const plane = new THREE.Mesh( new THREE.PlaneGeometry( 2, 12 ), new THREE.MeshBasicMaterial( { color: 0xa0a0a0 } ) );
			plane.position.set( 0,5, 0.0002 );
			group.add( plane );

			//靶子从外到里五种颜色
			const white = new THREE.Mesh( new THREE.CircleGeometry( 1, 64 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
			const black = new THREE.Mesh( new THREE.CircleGeometry( 0.8, 64 ), new THREE.MeshBasicMaterial( { color: 0x000000 } ) );
			const blue = new THREE.Mesh( new THREE.CircleGeometry( 0.6, 64 ), new THREE.MeshBasicMaterial( { color: 0x00ffff } ) );
			const red = new THREE.Mesh( new THREE.CircleGeometry( 0.4, 64 ), new THREE.MeshBasicMaterial( { color: 0xff0000 } ) );
			const yellow = new THREE.Mesh( new THREE.CircleGeometry( 0.2, 64 ), new THREE.MeshBasicMaterial( { color: 0xffd800 } ) );
			group.add( white );
			group.add( black );
			group.add( blue );
			group.add( red );
			group.add( yellow );
			white.position.z=0.0003;
			black.position.z=0.0004;
			blue.position.z=0.0005;
			red.position.z=0.0006;
			yellow.position.z=0.0007;
			
			scene.add( group );
			group.position.set(0, -2, -14 );
			group.rotation.set( -Math.PI/2, 0, Math.PI );
		}		
		/*
		* 显示成绩
		*/
		function setScore(score){
			scoreboard.clear();
			let text=0;
			if (book["game"+gameID]) text = book["game"+gameID];
			else book["game"+gameID]=0;
			if (score!=null){
				text = score + " / " + book["game"+gameID];
				if (question<10) text += " ("+(10-question)+")";
			}
			const textGeo = new TextGeometry( text+"", {
				font: font,
				size: 0.5,
				height: 0,
			} );
			textGeo.computeBoundingBox();

			const centerOffset = - 0.5 * ( textGeo.boundingBox.max.x - textGeo.boundingBox.min.x );

			const textMesh = new THREE.Mesh( textGeo, new THREE.MeshBasicMaterial( { color: 0x000000 } ) );

			textMesh.position.x = centerOffset;
			textMesh.position.y = -0.3;
			textMesh.position.z = 0.01;

			scoreboard.add( textMesh );

		}
		/*
		* 显示题目
		*/
		function setQuestion(text,font,size){
			questionBoard.clear();
			getTextMesh(text,font,size,questionBoard);
		}
		/*
		* 获取文字尺寸
		*/
		function getTextBox(text,font,size){
			//该方法效率极低，主要是生成TextGeometry费时太久
			//可替代的方案是根据字体文件中每个字符的尺寸来进行计算，
			//但是不知道字符间距是怎么定义的总是算不准，因此暂时搁置
			let box = new TextGeometry( text, {
				font: font,
				size: size,
				height: 0,
			} );
			//计算边界
			box.computeBoundingBox();
			return {w:box.boundingBox.max.x-box.boundingBox.min.x, h:box.boundingBox.max.y-box.boundingBox.min.y};		
		}
		/*
		* 获取文字对象
		*/
		function getTextMesh(otext,font,osize,plane){
			let text = otext;
			let size = osize;
			plane.geometry.computeBoundingBox();
			let w = plane.geometry.boundingBox.max.x-plane.geometry.boundingBox.min.x;
			let h = plane.geometry.boundingBox.max.y-plane.geometry.boundingBox.min.y;
			while(true){
				//获取边界
				let box = getTextBox(text,font,size);
				if (box.h>h*0.99){
					//超高
					//字体太小了就将下面的部分截断，用省略号代替
					if (size-h*0.01 <= h*0.05) {
						let lines = text.split('\n');
						let l = Math.floor(lines.length * (h / box.h));
						text = "";
						for(let i=0;i<l;i++) text += lines[i]+(i==l-1?"...":"")+'\n';
						break;
					}else{
						//重新进行换行处理
						size -= h*0.01;
						text = otext;
					}
				}else if (box.w>w*0.99) {
					//超宽
					let newText="";
					let lines = text.split('\n');
					//找出最宽的行
					let max = 0;
					for(let i=0;i<lines.length;i++){
						if (max<lines[i].length) max = lines[i].length;
					}
					//每行最多几个字母
					//TODO：没有考虑字母不同宽，以及中英文混合的问题
					let lc = Math.floor((w*0.98)/(box.w/max));
					if (lc==0) break;
					//逐行处理
					for(let i=0;i<lines.length;i++){
						//该行超宽
						if (lines[i].length>lc){
							//应该分几行
							let l = Math.ceil(lines[i].length/lc);
							if (font.data.familyName=="KaiTi"){
								//中文的换行处理
								//TODO日文的处理以及中英文混合的情况
								for(let j=0;j<l;j++){
									newText += lines[i].substr(j*lc,lc)+"\n";
								}
							}else{
								//其他语言的换行处理
								//TODO：字母不等距和没有空格的情况
								let words = lines[i].split(' ');
								let c=0;
								//少循环一次
								for(let j=0;j<words.length-1;j++){
									newText += words[j]+" ";
									c += words[j].length+1;
									if (c+words[j+1].length>=lc){
										//再加一个词就超宽时换行
										newText +="\n";
										c=0;
									}
								}
								//加上最后一个词
								newText += words[words.length-1];
							}
						}else newText +=lines[i]+"\n";
					}
					text = newText;
				}else break;
			}
			const textGeo = new TextGeometry( text, {
				font: font,
				size: size,
				height: 0,
			} );
			//自动居中
			textGeo.center();
			const textMesh =  new THREE.Mesh( textGeo, new THREE.MeshBasicMaterial( { color: 0x000000 } ) );
			plane.add( textMesh );
		}
		/*
		* 改变颜色突出显示正确选项
		*/
		function changeColor(loca){
			let textGeo = targets[loca].children[7].children[0];
			textGeo.material.color = new THREE.Color(0xff0000);
		}
		/*
		* 更改选项内容
		*/
		function updateTarget(text,font,size,loca,id){
			let plane = targets[loca].children[7];
			plane.clear();
			getTextMesh(text,font,size,plane);
			//给靶子设置id，用于判断是否选择了正确的
			targets[loca].tag = id;
		}
		/*
		* 改变屏幕大小
		*/
		function onWindowResize() {

			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();

			renderer.setSize( window.innerWidth, window.innerHeight );

		}
		/*
		* 载入json化的字体
		*/
		async function loadFont(font) {
			const p = await new Promise((resolve)=>{				
				const loader = new FontLoader();
				loader.load( m.current()+"fonts/"+font+'.json', resolve );
		  });
		  return p;
		}		
		/*
		* 载入弓箭、冰壶
		*/
		async function loadGlb(obj){
			const p = await new Promise((resolve)=>{				
				const loader = new GLTFLoader();
				//载入环境
				loader.load( m.current() + "models/" + obj+'.glb', ( gltf ) => {
					
					//场景中添加环境
					scene.add( gltf.scene );
					gltf.scene.traverse( child => {
						//为子对象设置阴影
						if ( child.isMesh ) {
							child.castShadow = true;
							child.receiveShadow = true;
						}
					} );
					
					resolve(gltf.scene);
				} );
		  });
		  return p;
		}
		/*
		* 获取渐变色
		*/
		function getColor(v){
	    let sR = -2.55;
	    let sG = 2.55;
			return 'rgb('+ parseInt((sR * v + 255))+ ',' + parseInt((sG * v))+ ',0)';	
		}			
		/*
		* 射箭选择了错误的选项或没有选择
		*/
		function wrong(){
			clearTimeout(playTimerID);
			//播放啊哦
			sound[0].play();
			setScore(score);
			if (items[gameID-1].type==1){
				//高亮正确选项
				for(let i=0;i<4;i++){
					if (targets[i].tag == answer) changeColor(i);
				}
				//稍等片刻让用户看清
				setTimeout(next,1000);
			}else{
				setTimeout(next,500);
			}
		}
		/*
		* 射箭计分
		*/
		function scoring(){
			if (isRight){
				//剩余时间乘以距离圆心的百分比
				score += Math.floor(timerVal*(1-offset));
				setScore(score);
				setTimeout(next,500);
			}else wrong();
		}
		/*
		* 听音找字
		*/
		function game1(){
			//选出选项
			const tmp = getOptionArr(book.zis.length);
			//显示选项
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].zi,font,1.5,i,tmp[i]);
			}
			//播放发音
			playing = ziVoice[answer*2+(isMan?0:1)];
			playing.play();
		}
		/*
		* 看字读音
		*/
		function game2(){
			//选出选项
			answer = getRandom(book.zis.length);
			pinyin.type = "word.eval.cn";
			pinyin.z = book.zis[answer].zi;
			pinyin.p = book.zis[answer].sound;
			setQuestion(book.zis[answer].zi,font,0.15);
		}
		/*
		* 看字找意
		*/
		function game3(){
			const tmp = getOptionArr(book.zis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].m.replaceAll(";","\n"),otherFont,0.2,i,tmp[i]);
			}
			//显示题目
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.zis[answer].zi,font,0.15);
		}
		/*
		* 看意找字
		*/
		function game4(){
			const tmp = getOptionArr(book.zis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].zi,font,1.5,i,tmp[i]);
			}
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.zis[answer].m.replaceAll(";","\n"),otherFont,0.04);
		}
		/*
		* 听字找意
		*/
		function game5(){
			const tmp = getOptionArr(book.zis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].m.replaceAll(";","\n"),otherFont,0.2,i,tmp[i]);
			}
			playing = ziVoice[answer*2+(isMan?0:1)];
			playing.play();
		}
		/*
		* 听音找词
		*/
		function game6(){
			const tmp = getOptionArr(book.cis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.cis[tmp[i]].ci,font,0.7,i,tmp[i]);
			}
			playing = ciVoice[answer*2+(isMan?0:1)];
			playing.play();
		}
		/*
		* 看词读音
		*/
		function game7(){
			//选出选项
			answer = getRandom(book.zis.length);
			pinyin.type = "sent.eval.cn";
			pinyin.z = book.cis[answer].zi;
			pinyin.p = book.cis[answer].py.replaceAll("-"," ");
			setQuestion(book.cis[answer].ci,font,0.1);
		}
		/*
		* 看词找意
		*/
		function game8(){
			const tmp = getOptionArr(book.cis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.cis[tmp[i]].m.replaceAll(";","\n"),otherFont,0.2,i,tmp[i]);
			}
			//显示题目
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.cis[answer].ci,font,0.07);
		}
		/*
		* 看意找词
		*/
		function game9(){
			const tmp = getOptionArr(book.cis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.cis[tmp[i]].ci,font,0.7,i,tmp[i]);
			}
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.cis[answer].m.replaceAll(";","\n"),otherFont,0.04);
		}
		/*
		* 听词找意
		*/
		function game10(){
			const tmp = getOptionArr(book.cis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.cis[tmp[i]].m.replaceAll(";","\n"),otherFont,0.2,i,tmp[i]);
			}
			playing = ciVoice[answer*2+(isMan?0:1)];
			playing.play();
		}
		/*
		* 看意找句
		*/
		function game11(){
			const tmp = getOptionArr(book.jus.length);
			for(let i=0;i<4;i++){
				updateTarget(book.jus[tmp[i]].m,otherFont,0.2,i,tmp[i]);
			}
			//显示题目
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.jus[answer].ju.replaceAll(" ",""),font,0.05);
		}
		/*
		* 看句读音
		*/
		function game12(){
			answer = getRandom(book.jus.length);
			pinyin.type = "sent.eval.cn";
			pinyin.z = book.jus[answer].ju.replaceAll(" ","");
			pinyin.p = book.jus[answer].py.replaceAll("-"," ");
			setQuestion(pinyin.z,font,0.07);
		}
		/*
		* 听音找句
		*/
		function game13(){
			answer = getRandom(book.jus.length);
			let jid = book.jus[answer].id;
			playing = new Howl({
			  src: ['https://debug.smwho.com/sound/ju1/'+(parseInt(book.jus[answer].sex)%2==0?"f":"m")+'/'+parseInt(jid/1000)+"/"+jid+'.mp3'],
			  autoplay: true,
			  onend:juVoiceEnd
			});
			
			let tmp=[];
			for(let i=0;i<4;i++){
				while(true){
					let r = random(4);
					if (tmp.indexOf(r)==-1) {
						tmp.push(r);
						break;
					}
				}
			}
			updateTarget(book.jus[answer].ju.replaceAll(" ",""),font,0.5,tmp[0],answer);
			for(let j=0;j<book.wrong.length;j+=3){
				if (book.wrong[j].jid == jid){
					for(let i=1;i<4;i++)
						updateTarget(book.wrong[j+i-1].ju,font,0.5,tmp[i],-1);
				}
			}
		}
		/*
		* 看音找字
		*/
		function game14(){
			const tmp = getOptionArr(book.zis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].zi,font,1.5,i,tmp[i]);
			}
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.zis[answer].py,pinyinFont,0.15);
		}
		/*
		* 看音读音
		*/
		function game15(){
			answer = getRandom(book.zis.length);
			pinyin.type = "word.eval.cn";
			pinyin.z = book.zis[answer].zi;
			pinyin.p = book.zis[answer].sound;
			setQuestion(book.zis[answer].py,pinyinFont,0.15);
		}
		/*
		* 看字找音
		*/
		function game16(){
			const tmp = getOptionArr(book.zis.length);
			for(let i=0;i<4;i++){
				updateTarget(book.zis[tmp[i]].py,pinyinFont,0.8,i,tmp[i]);
			}
			questionBoard.position.set( 0, -0.3, -1 );
			setQuestion(book.zis[answer].zi,font,0.15);
		}
		/*
		* 本轮游戏结束
		*/
		function endGame(){
			gaming = false;
			//TODO:打破记录的奖励动画和欢呼声
			if (score>book["game"+gameID]) {
				let honor = m.loadInt("honor");
				honor += score-book["game"+gameID];
				m.setHonor(honor);
				book["game"+gameID] = score;
				m.saveJson("books",books);
				//TODO:打破世界记录的奖励动画和双倍荣誉，以及NFT证书
			}
			if (items[gameID-1].type==1){
				//隐藏箭
				arrow.position.set( 0.15, -0.6, 10 );
				//将其余选项牌隐藏，显示下一轮开始的选项
				for(let i=0;i<4;i++){
					if (i==0){
						targets[i].position.set(0,0.5,-10);
						updateTarget("开始",font,0.7,0);
					}else{
						targets[i].position.set(i*3-5, 0.5, 10);
					}
				}
				questionBoard.position.set( 0, 0.1, 1 );
			}else{
				//镜头和冰壶复位
				camera.position.set(0,0,0);
				camera.rotation.set(0,0,0);
				curling.position.set( 2, -1.8, -4 );
				setQuestion("再挑战",font,0.1);
				window.addEventListener( 'mouseup', onPointerup );
				window.addEventListener( 'touchend', onPointerup );
				window.addEventListener( 'mousedown', onPointerdown );
				window.addEventListener( 'touchstart', onPointerdown );
			}
		}
		/*
		* 设置四个候选项和一个正确答案
		*/
		function getOptionArr(size){
			var tmp = new Array(4);
			//选出正确答案
			answer = getRandom(size);
			//为正确答案选择位置
			tmp[random(4)] = answer;
			for(let i=0;i<3;i++){
				while(true){
					//决定其他选项的摆放位置
					let j=random(4);
					if (tmp[j] == null){
						while(true){
							let k=random(size);
							//选择其他选项，排除掉发音相同或者意思相同的
							if (tmp.indexOf(k)==-1 && isIncloud(k)){
								tmp[j] = k;
								break;
							}
						}
						break;
					}
				}
			}
			return tmp;
		}
		/*
		* 生成随机数
		*/
		function random(size){
			return Math.floor(Math.random()*size);
		}
		/*
		* 挑选正确选项
		*/
		function getRandom(size){
			while(true){
				let r = random(size);
				if (randomArr.indexOf(r)==-1) {
					randomArr.push(r);
					return r;
				}
			}
		}		
		/*
		* 判断备选项是否有重复的
		*/
		function isIncloud(k){
			if (gameID >=6 && gameID <=10){
				return book.cis[answer].py != book.cis[k].py && book.cis[answer].m != book.cis[k].m;
			}else if (gameID >=11 && gameID <=13){
				return book.jus[answer].m != book.jus[k].m;
			}else{
				return book.zis[answer].sound != book.zis[k].sound && book.zis[answer].m != book.zis[k].m;
			}
		}		
		/*
		* 更新进度条的长度和颜色
		*/
		function setTimebar(v){
			timerBar.material.color = new THREE.Color(getColor(v));
			timerBar.geometry = new THREE.CylinderGeometry( 0.01, 0.01, v/100, 32 );
		}
		/*
		* 渲染
		*/
		function render(){
			renderer.render( scene, camera );
		}
		/*
		* 动画
		*/
		function animate() {

			TWEEN.update();
			render();
			animationID = requestAnimationFrame( animate );

		}
		/**
		* 清除模型，模型中有 group 和 scene,需要进行判断
		* @param scene
		* @returns
		*/
		function clearScene(){
			// 从scene中删除模型并释放内存
			if(objects.length > 0){		
				for(var i = 0; i< objects.length; i++){
					var currObj = objects[i];
					
					// 判断类型
					if(currObj instanceof THREE.Scene){
						var children = currObj.children;
						for(var i = 0; i< children.length; i++){
							deleteGroup(children[i]);
						}	
					}else{				
						deleteGroup(currObj);
					}
					scene.remove(currObj);
				}
			}
		}
		/*
		* 删除group，释放内存
		*/
		function deleteGroup(group) {
	    if (!group) return;
	    // 删除掉所有的模型组内的mesh
	    group.traverse(function (item) {
	        if (item instanceof THREE.Mesh) {
	            item.geometry.dispose(); // 删除几何体
	            item.material.dispose(); // 删除材质
	        }
	    });
		}
		/*
		* 释放内存
		*/
		function destroy(){
			cancelAnimationFrame(animationID);
			clearTimeout(gameTimerID);
			clearTimeout(playTimerID);
			clearScene();
			m.recCancel();
			m.recClose();
			scene.clear();
			scene.background = null;
			renderer.dispose();
			let gl = renderer.domElement.getContext("webgl");
			gl && gl.getExtension("WEBGL_lose_context").loseContext();
			renderer.forceContextLoss();
			renderer.content = null;
			if (playing) playing.stop();

			m.getNode("goback").removeEventListener('pointerdown', goback);
			window.removeEventListener( 'mouseup', onMouseup );
			window.removeEventListener( 'touchend', onMouseup );
			window.removeEventListener( 'mousedown', onMousedown );
			window.removeEventListener( 'touchstart', onMousedown );
			window.removeEventListener( 'mousemove', onMousemove );
			window.removeEventListener( 'touchmove', onMousemove );
			window.removeEventListener( 'mouseup', onPointerup );
			window.removeEventListener( 'touchend', onPointerup );
			window.removeEventListener( 'mousedown', onPointerdown );
			window.removeEventListener( 'touchstart', onPointerdown );
			window.removeEventListener( 'mousemove', onPointermove );
			window.removeEventListener( 'touchmove', onPointermove );
			window.removeEventListener( 'resize', onWindowResize );
			container.removeChild(renderer.domElement);
			scene = null;
			camera = null;
			renderer = null;
			objects = null;
		}		
	}
}
export { Game };